package design.pattern.Lu15CompositePattern;

import com.alibaba.fastjson.JSON;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * @program JavaBase
 * @description: 组合模式 - 决策树
 * @author: zhanglu
 * @create: 2022-05-16 15:14:00
 */
@Slf4j
public class CompositePattern05 {

    /********************规则树数据模型********************/

    //规则树-聚合-整棵树
    @Data
    static class TreeRich{
        private TreeRoot treeRoot;                  //树根信息
        private Map<Long, TreeNode> treeNodeMap;    //树节点ID -> 子节点
    }

    //决策结果
    @Data
    @Builder
    @Accessors(chain = true)
    @AllArgsConstructor
    @NoArgsConstructor
    static class EngineResult{
        private boolean isSuccess;  //执行结果
        private String userId;      //用户ID
        private Long treeId;        //规则树ID
        private Long nodeId;        //果实节点ID
        private String nodeValue;   //果实节点值
    }

    //规则树根节点信息
    @Data
    static class TreeRoot{
        private Long treeId;            //规则树ID
        private Long treeRootNodeId;    //规则树根节点ID
        private String treeName;        //规则树名称
    }

    //规则树节点信息
    @Data
    static class TreeNode{
        private Long treeId;       //树ID
        private Long treeNodeId;   //树节点ID
        private Integer nodeType;  //节点类型：1-子叶、2-果实
        private String nodeValue;  //节点值 nodeType=2：果实值
        private String ruleKey;    //规则key
        private String ruleDesc;   //规则描述
        private List<TreeNodeLink> treeNodeLinkList;    //节点链路
    }

    //规则树-连线信息
    @Data
    static class TreeNodeLink{
        private Long nodeIdFrom;        //节点from
        private Long nodeIdTo;          //节点to
        private Integer ruleLimitType;  //限定类型 1:=;2:>;3:<;4:>=;5<=;6:enum[枚举范围]
        private String ruleLimitValue;  //限定值
    }

    /********************规则树逻辑处理规则构建********************/
    //逻辑决策-接口定义
    interface LogicFilter{
        /**
         * 逻辑决策器
         * @param matterValue   决策值
         * @param linkList      决策节点
         * @return              下一个节点ID
         */
        Long filter(String matterValue, List<TreeNodeLink> linkList);

        /**
         * 获取决策果实
         * @param treeId            树ID
         * @param userId            用户ID
         * @param decisionMatter    决策物料
         * @return                  决策果实
         */
        String matterValue(Long treeId, String userId, Map<String, String> decisionMatter);
    }

    //基础逻辑处理器-模版方法
    abstract static class BaseLogic implements LogicFilter{
        @Override
        public Long filter(String matterValue, List<TreeNodeLink> linkList) {
            for (TreeNodeLink nodeLink : linkList) {
                if(decisionLogic(matterValue, nodeLink)){
                    return nodeLink.getNodeIdTo();
                }
            }
            return 0L;
        }

        @Override
        public abstract String matterValue(Long treeId, String userId, Map<String, String> decisionMatter);

        //决策逻辑 - 模版方法
        private boolean decisionLogic(String matterValue, TreeNodeLink nodeLink){
            switch (nodeLink.getRuleLimitType()){
                case 1:
                    return matterValue.equals(nodeLink.getRuleLimitValue());
                case 2:
                    return new BigDecimal(matterValue).compareTo(new BigDecimal(nodeLink.getRuleLimitValue())) > 0;
                case 3:
                    return new BigDecimal(matterValue).compareTo(new BigDecimal(nodeLink.getRuleLimitValue())) < 0;
                case 4:
                    return new BigDecimal(matterValue).compareTo(new BigDecimal(nodeLink.getRuleLimitValue())) <= 0;
                case 5:
                    return new BigDecimal(matterValue).compareTo(new BigDecimal(nodeLink.getRuleLimitValue())) >= 0;
                default:
                    return false;
            }
        }
    }

    //用户性别决策
    static class UserGenderFilter extends BaseLogic{
        @Override
        public String matterValue(Long treeId, String userId, Map<String, String> decisionMatter) {
            return decisionMatter.get("gender");
        }
    }

    //用户年龄决策
    static class UserAgeFilter extends BaseLogic{
        @Override
        public String matterValue(Long treeId, String userId, Map<String, String> decisionMatter) {
            return decisionMatter.get("age");
        }
    }

    /********************规则树业务逻辑处理********************/

    //决策树配置
    static class EngineConfig{
        public static Map<String, LogicFilter> LOGIC_FILTER_MAP;
        {
            LOGIC_FILTER_MAP = new ConcurrentHashMap<>();
            LOGIC_FILTER_MAP.put("userAge", new UserAgeFilter());
            LOGIC_FILTER_MAP.put("userGender", new UserGenderFilter());
        }
    }

    //决策
    interface IEngine{
        /**
         * 决策树逻辑执行引擎
         * @param treeId            //树ID
         * @param userId            //用户ID
         * @param decisionMatter    //决策物料
         * @return
         */
        EngineResult process(Long treeId, String userId, Map<String, String> decisionMatter) throws Exception;
    }

    //执行抽象-模版方法
    @Slf4j
    abstract static class EngineBase extends EngineConfig implements IEngine{

        public abstract EngineResult process(Long treeId, String userId, Map<String, String> decisionMatter) throws Exception;

        //模版方法
        protected TreeNode engineDecisionMaker(TreeRich treeRich, Long treeId, String userId, Map<String, String> decisionMatter) throws Exception {
            TreeRoot treeRoot = treeRich.getTreeRoot();
            Map<Long, TreeNode> treeNodeMap = treeRich.getTreeNodeMap();
            //从规则树根节点开始遍历
            TreeNode treeNodeInfo = treeNodeMap.get(treeRoot.getTreeRootNodeId());
            //节点类型[NodeType]；1子叶、2果实
            while (treeNodeInfo.getNodeType() == 1){
                String ruleKey = treeNodeInfo.getRuleKey();
                LogicFilter logicFilter = LOGIC_FILTER_MAP.get(ruleKey);
                String matterValue = logicFilter.matterValue(treeId, userId, decisionMatter);
                Long nextNodeId = logicFilter.filter(matterValue, treeNodeInfo.getTreeNodeLinkList());
                treeNodeInfo = treeNodeMap.get(nextNodeId);
                if(treeNodeInfo == null){
                    log.info("决策树引擎=>null");
                    break;
                }
                log.info("决策树引擎=>{} userId：{} treeId：{} treeNode：{} ruleKey：{} matterValue：{}", treeRoot.getTreeName(), userId, treeId, treeNodeInfo.getTreeNodeId(), ruleKey, matterValue);
            }
            return treeNodeInfo;
        }

    }

    /**
     * 规则树-控制器
     */
    static class TreeEngineHandle extends EngineBase{
        //决策树
        private Class<? extends TreeRichBuilder> treeRichClass;

        public TreeEngineHandle(Class<? extends TreeRichBuilder> treeRichClass) {
            this.treeRichClass = treeRichClass;
        }
        @Override
        public EngineResult process(Long treeId, String userId, Map<String, String> decisionMatter) throws Exception {
            TreeRichBuilder treeRichBuilder = JDKProxyFactory.getProxy(treeRichClass);
            TreeRich treeRich = treeRichBuilder.initTreeRich();
            TreeNode treeNode = engineDecisionMaker(treeRich, treeId, userId, decisionMatter);
            return EngineResult.builder()
                    .userId(userId).treeId(treeId)
                    .nodeId(treeNode.getTreeNodeId())
                    .nodeValue(treeNode.getNodeValue())
                    .isSuccess(true)
                    .build();
        }
    }

    /********************工厂模式构建不同的决策树模型(数据初始化)********************/

    //决策树构建
    interface TreeRichBuilder{
        TreeRich initTreeRich();
    }

    static class GoldRewardTreeRich implements TreeRichBuilder{
        public GoldRewardTreeRich(){}
        @Override
        public TreeRich initTreeRich() {
            // 节点：1
            TreeNode treeNode_01 = new TreeNode();
            treeNode_01.setTreeId(10001L);
            treeNode_01.setTreeNodeId(1L);
            treeNode_01.setNodeType(1);
            treeNode_01.setNodeValue(null);
            treeNode_01.setRuleKey("userGender");
            treeNode_01.setRuleDesc("用户性别[男/女]");

            // 链接：1->11
            TreeNodeLink treeNodeLink_11 = new TreeNodeLink();
            treeNodeLink_11.setNodeIdFrom(1L);
            treeNodeLink_11.setNodeIdTo(11L);
            treeNodeLink_11.setRuleLimitType(1);
            treeNodeLink_11.setRuleLimitValue("man");

            // 链接：1->12
            TreeNodeLink treeNodeLink_12 = new TreeNodeLink();
            treeNodeLink_12.setNodeIdFrom(1L);
            treeNodeLink_12.setNodeIdTo(12L);
            treeNodeLink_12.setRuleLimitType(1);
            treeNodeLink_12.setRuleLimitValue("woman");

            List<TreeNodeLink> treeNodeLinkList_1 = new ArrayList<>();
            treeNodeLinkList_1.add(treeNodeLink_11);
            treeNodeLinkList_1.add(treeNodeLink_12);

            treeNode_01.setTreeNodeLinkList(treeNodeLinkList_1);

            // 节点：11
            TreeNode treeNode_11 = new TreeNode();
            treeNode_11.setTreeId(10001L);
            treeNode_11.setTreeNodeId(11L);
            treeNode_11.setNodeType(1);
            treeNode_11.setNodeValue(null);
            treeNode_11.setRuleKey("userAge");
            treeNode_11.setRuleDesc("用户年龄");

            // 链接：11->111
            TreeNodeLink treeNodeLink_111 = new TreeNodeLink();
            treeNodeLink_111.setNodeIdFrom(11L);
            treeNodeLink_111.setNodeIdTo(111L);
            treeNodeLink_111.setRuleLimitType(3);
            treeNodeLink_111.setRuleLimitValue("25");

            // 链接：11->112
            TreeNodeLink treeNodeLink_112 = new TreeNodeLink();
            treeNodeLink_112.setNodeIdFrom(11L);
            treeNodeLink_112.setNodeIdTo(112L);
            treeNodeLink_112.setRuleLimitType(5);
            treeNodeLink_112.setRuleLimitValue("25");

            List<TreeNodeLink> treeNodeLinkList_11 = new ArrayList<>();
            treeNodeLinkList_11.add(treeNodeLink_111);
            treeNodeLinkList_11.add(treeNodeLink_112);

            treeNode_11.setTreeNodeLinkList(treeNodeLinkList_11);

            // 节点：12
            TreeNode treeNode_12 = new TreeNode();
            treeNode_12.setTreeId(10001L);
            treeNode_12.setTreeNodeId(12L);
            treeNode_12.setNodeType(1);
            treeNode_12.setNodeValue(null);
            treeNode_12.setRuleKey("userAge");
            treeNode_12.setRuleDesc("用户年龄");

            // 链接：12->121
            TreeNodeLink treeNodeLink_121 = new TreeNodeLink();
            treeNodeLink_121.setNodeIdFrom(12L);
            treeNodeLink_121.setNodeIdTo(121L);
            treeNodeLink_121.setRuleLimitType(3);
            treeNodeLink_121.setRuleLimitValue("25");

            // 链接：12->122
            TreeNodeLink treeNodeLink_122 = new TreeNodeLink();
            treeNodeLink_122.setNodeIdFrom(12L);
            treeNodeLink_122.setNodeIdTo(122L);
            treeNodeLink_122.setRuleLimitType(5);
            treeNodeLink_122.setRuleLimitValue("25");

            List<TreeNodeLink> treeNodeLinkList_12 = new ArrayList<>();
            treeNodeLinkList_12.add(treeNodeLink_121);
            treeNodeLinkList_12.add(treeNodeLink_122);

            treeNode_12.setTreeNodeLinkList(treeNodeLinkList_12);

            // 节点：111
            TreeNode treeNode_111 = new TreeNode();
            treeNode_111.setTreeId(10001L);
            treeNode_111.setTreeNodeId(111L);
            treeNode_111.setNodeType(2);
            treeNode_111.setNodeValue("果实A");

            // 节点：112
            TreeNode treeNode_112 = new TreeNode();
            treeNode_112.setTreeId(10001L);
            treeNode_112.setTreeNodeId(112L);
            treeNode_112.setNodeType(2);
            treeNode_112.setNodeValue("果实B");

            // 节点：121
            TreeNode treeNode_121 = new TreeNode();
            treeNode_121.setTreeId(10001L);
            treeNode_121.setTreeNodeId(121L);
            treeNode_121.setNodeType(2);
            treeNode_121.setNodeValue("果实C");

            // 节点：122
            TreeNode treeNode_122 = new TreeNode();
            treeNode_122.setTreeId(10001L);
            treeNode_122.setTreeNodeId(122L);
            treeNode_122.setNodeType(2);
            treeNode_122.setNodeValue("果实D");

            // 树根
            TreeRoot treeRoot = new TreeRoot();
            treeRoot.setTreeId(10001L);
            treeRoot.setTreeRootNodeId(1L);
            treeRoot.setTreeName("规则决策树");

            Map<Long, TreeNode> treeNodeMap = new HashMap<>();
            treeNodeMap.put(1L, treeNode_01);
            treeNodeMap.put(11L, treeNode_11);
            treeNodeMap.put(12L, treeNode_12);
            treeNodeMap.put(111L, treeNode_111);
            treeNodeMap.put(112L, treeNode_112);
            treeNodeMap.put(121L, treeNode_121);
            treeNodeMap.put(122L, treeNode_122);
            TreeRich treeRich = new TreeRich();
            treeRich.setTreeRoot(treeRoot);
            treeRich.setTreeNodeMap(treeNodeMap);
            return treeRich;
        }
    }

    static class CouponTreeRich implements TreeRichBuilder{
        @Override
        public TreeRich initTreeRich() {
            return null;
        }
    }

    //工厂代理类
    static class JDKProxyFactory {
        public static <T> T getProxy(Class<? extends T> aClass) throws Exception {
//            JDKInvocationHandler handler = new JDKInvocationHandler(aClass.newInstance());
            //jdk动态代理
            InvocationHandler handler = (proxy, method, args) -> {
                return method.invoke(aClass.getConstructor().newInstance(), args);
            };
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            Class<?>[] classes = aClass.getInterfaces();
            return (T) Proxy.newProxyInstance(classLoader, new Class[]{classes[0]}, handler);
        }
    }


    //jdk动态代理
    static class JDKInvocationHandler implements InvocationHandler {
        private Object obj;
        public JDKInvocationHandler(Object obj){
            this.obj = obj;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            return method.invoke(obj, args);
        }
    }

    //工具类
    static class ClassLoaderUtils {
        public static Class<?>[] getClazzByArgs(Object[] args) {
            Class<?>[] parameterTypes = new Class[args.length];
            for (int i = 0; i < args.length; i++) {
                if (args[i] instanceof ArrayList) {
                    parameterTypes[i] = List.class;
                    continue;
                }
                if (args[i] instanceof LinkedList) {
                    parameterTypes[i] = List.class;
                    continue;
                }
                if (args[i] instanceof HashMap) {
                    parameterTypes[i] = Map.class;
                    continue;
                }
                if (args[i] instanceof Long){
                    parameterTypes[i] = long.class;
                    continue;
                }
                if (args[i] instanceof Double){
                    parameterTypes[i] = double.class;
                    continue;
                }
                if (args[i] instanceof TimeUnit){
                    parameterTypes[i] = TimeUnit.class;
                    continue;
                }
                parameterTypes[i] = args[i].getClass();
            }
            return parameterTypes;
        }
    }

    /********************测试********************/

    @Test
    public void test01() throws Exception {
        IEngine engine = new TreeEngineHandle(GoldRewardTreeRich.class);
        Map<String, String> decisionMatter = new HashMap<>();
        decisionMatter.put("gender", "man");
        decisionMatter.put("age", "29");

        EngineResult result = engine.process(10001L, "Oli09pLkdjh", decisionMatter);
        log.info("测试结果：{}", JSON.toJSONString(result));
    }

}
